%{
#include <stdio.h>
#include <ctype.h>
#include <malloc.h>
#include "atom.h"
#include "dtd2.h"
#include "dtd.h"

extern int yygetlineno();
void yyerror(char *s);

static void GenerateParents();
static void OrganizeAttrs();
static void CreateDefFile();
static void CreateTabFile();
static void newElement(atom name, int type1, int type2, List *list);
static DTDAttribute *newAttribute(atom name, int type, unsigned int valid,
				  atom dataType, List *list, char *defString,
				  atom defAtom);
static void insertAttr(atom name, List *list);

%}

%union {
    char *string;
    int opttype;
    int attrtype;
    List *list;
    DTDAttribute *attribute;
}

%token ELESTART ATTSTART ATTFIXED ATTIMPLIED ATTREQUIRED EMPTYLIST
%token <string> WORD QUOTE
%type <opttype> eletype
%type <list> list slist clist attlist
%type <list> agroup alist
%type <string> aword
%type <attribute> att
%left '|' '&' ','


%%
top		: element
		    {
		    }
		| top element
		    {
		    }
		| attribute
		    {
		    }
		| top attribute
		    {
		    }

		| top error
		    {
		    }
		;

element		: ELESTART slist eletype eletype clist '>'
		    {
			atom at;

			while ((at = PopList($2)) != 0) {
			    newElement(at, $3, $4, $5);
			}
		    }
		| ELESTART slist eletype eletype clist '+' '>'
		    {
			atom at;

			while ((at = PopList($2)) != 0) {
			    newElement(at, $3, $4, $5);
			}
		    }
		| ELESTART slist eletype eletype EMPTYLIST '>'
		    {
			atom at;

			while ((at = PopList($2)) != 0) {
			    newElement(at, $3, $4, 0);
			}
		    }
		;

slist		: WORD
		    {
			atom at = GetAtom($1);
			$$ = AddList(0, at);
		    }
		| slist '|' slist
		    {
			$$ = sumLists($1, $3);
		    }
		| '(' slist ')'
		    {
			$$ = $2;
		    }
		;


clist		: list
		    {
			$$ = $1;
		    }
		| clist listplus list
		    {
			$$ = sumLists($1, $3);
		    }
		| clist listminus list
		    {
			$$ = subLists($1, $3);
		    }
		;
		    
list		: WORD
		    {
			atom at = GetAtom($1);
			$$ = AddList(0, at);
		    }
		| list '|' list
		    {
			$$ = sumLists($1, $3);
		    }
		| list ',' list
		    {
			$$ = sumLists($1, $3);
		    }
		| list '&' list
		    {
			$$ = sumLists($1, $3);
		    }
		| '(' list '+' ')'
		    {
			$$ = $2;
		    }
		| '(' list ')'
		    {
			$$ = $2;
		    }
		;

listplus	: '+'
		| '+' '+'
		;

listminus	: '+' '-'
		| '-'
		;

eletype		: '-'
		    {
			$$ = 0;
		    }
		| 'O'
		    {
			$$ = 1;
		    }
		;

attribute	: ATTSTART slist attlist '>'
		    {
			atom at;

			while ((at = PopList($2)) != 0) {
			    insertAttr(at, $3);
			}
		    }
		;

attlist		: att
		    {
			$$ = AddList(0, (int)$1);
		    }
		| attlist att
		    {
			$$ = AddList($1, (int)$2);
		    }
		;

att		: WORD aword error
		    {
			fprintf(stderr, "%d: Unknown attribute type\n",
				yygetlineno());
		    }
		| WORD aword WORD
		    {
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_DATATYPE | AF_DEFATOM,
					  GetAtom($2), 0, 0, GetAtom($3));
		    }
		| WORD aword QUOTE 
		    {
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_DATATYPE | AF_DEFSTRING,
					  GetAtom($2), 0, $3, 0);
		    }
		| WORD aword ATTREQUIRED
		    {
			$$ = newAttribute(GetAtom($1), A_REQUIRED,
					  AF_DATATYPE,
					  GetAtom($2), 0, 0, 0);
		    }
		| WORD aword ATTIMPLIED
		    {
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_DATATYPE,
					  GetAtom($2), 0, 0, 0);
		    }
		| WORD aword ATTFIXED QUOTE
		    {
			$$ = newAttribute(GetAtom($1), A_FIXED,
					  AF_DATATYPE | AF_DEFSTRING,
					  GetAtom($2), 0, $4, 0);
		    }
		| WORD alist error
		    {
			fprintf(stderr, "%d: Unknown attribute type\n",
				yygetlineno());
		    }
		| WORD alist WORD
		    {
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_LIST | AF_DEFATOM,
					  0, $2, 0, GetAtom($3));
		    }
		| WORD alist QUOTE 
		    {
			/* Unlikely to ever show up */
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_LIST | AF_DEFSTRING,
					  0, $2, $3, 0);
		    }
		| WORD alist ATTREQUIRED
		    {
			$$ = newAttribute(GetAtom($1), A_REQUIRED,
					  AF_LIST,
					  0, $2, 0, 0);
		    }
		| WORD alist ATTIMPLIED
		    {
			$$ = newAttribute(GetAtom($1), A_IMPLIED,
					  AF_LIST,
					  0, $2, 0, 0);
		    }
		| WORD alist ATTFIXED QUOTE
		    {
			/* Unlikely to ever show up */
			$$ = newAttribute(GetAtom($1), A_FIXED,
					  AF_LIST | AF_DEFSTRING,
					  0, $2, $4, 0);
		    }
		;

aword		: WORD
		    {
			$$ = $1;
		    }
		;

alist		: '(' WORD ')'
		    {
			atom at = GetAtom($2);
			$$ = AddList(0, at);
		    }
		| '(' agroup ')'
		    {
			$$ = $2;
		    }
		;

agroup		: WORD '|' WORD
		    {
			atom at = GetAtom($1);
			atom at2 = GetAtom($3);

			$$ = AddList(0, at);
			$$ = AddList($$, at2);
		    }
		| agroup '|' WORD
		    {
			atom at2 = GetAtom($3);

			$$ = AddList($1, at2);
		    }
		;
%%

extern FILE *yyin;

main()
{
    while (!feof(yyin)) {
	yyparse();
    }
    GenerateParents();
    OrganizeAttrs();
    CreateDefFile();
    CreateTabFile();
    exit(0);
}

void
yyerror(char *s)
{
    fprintf(stderr, "%d: %s\n", yygetlineno(), s);
}

static DTDElement *eTable[5000];

static void
CreateDefFile()
{
    int i;
    FILE *fp, *fopen();

    fp = fopen("dtddefs.h", "w");
    if (fp == NULL) {
	perror("dtddefs.h");
	return;
    }

    fprintf(fp, "/*\n * This file automatically generated\n */\n\n");
    for (i = 1; GetAtomVal(i); i++) {
	fprintf(fp, "#define %-10.10s  %d\n", GetAtomVal(i), i);
    }
    fprintf(fp, "#define MAXELEMENT\t%d\n", i);
}

static void
CreateTabFile()
{
    int i, j, k;
    FILE *fp, *fopen();

    fp = fopen("dtdtab.h", "w");
    if (fp == NULL) {
	perror("dtdtab.h");
	return;
    }

    fprintf(fp, "/*\n * This file automatically generated\n */\n\n");
    fprintf(fp, "#include \"dtd2.h\"\n\n");

    /* Create an array of atom names */

    fprintf(fp, "static char *atoms[] = {\n\t0,\n");
    for (i = 1; GetAtomVal(i); i++) {
	fprintf(fp, "\t\"%s\",\n", GetAtomVal(i));
    }
    fprintf(fp, "};\n");

    /* Create an array of elements */
    fprintf(fp, "\nDTDElement eTable[] = {\n");
    for (i = 1; GetAtomVal(i); i++) {
	if (eTable[i]) {
	    fprintf(fp, "    { %s, 0x%x,\n      { ",
		    GetAtomVal(i),
		    eTable[i]->flags);
	    /* children */
	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->children[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 20; j++) {
		atom at = eTable[i]->children[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 30; j++) {
		atom at = eTable[i]->children[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 40; j++) {
		atom at = eTable[i]->children[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n      },\n      { ");
	    /* parents */
	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->parents[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 20; j++) {
		atom at = eTable[i]->parents[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 30; j++) {
		atom at = eTable[i]->parents[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n        ");
	    for ( ; j < 40; j++) {
		atom at = eTable[i]->parents[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,", GetAtomVal(at));
	    }
	    fprintf(fp, "\n      },\n      {\n");
	    for (j = 0; j < 10; j++) {
		DTDAttribute *ap = &(eTable[i]->attrs[j]);
		fprintf(fp, "        { ");
		if (ap->name != 0) {
		    char *str;
		    int k;

		    switch (ap->type) {
			case A_REQUIRED:
			    str = "A_REQUIRED";
			    break;
			case A_FIXED:
			    str = "A_FIXED";
			    break;
			case A_IMPLIED:
			    str = "A_IMPLIED";
			    break;
		    }

		    fprintf(fp, "  %s, %s, 0x%x, ",
			    GetAtomVal(ap->name), str, ap->valid);

		    if (ap->dataType)
			fprintf(fp, "%s,\n            {",
				GetAtomVal(ap->dataType));
		    else
			fprintf(fp, "0,\n            {");

		    for (k = 0; k < 10; k++) {
			if (ap->list[k])
			    fprintf(fp, " %s,",
				    GetAtomVal(ap->list[k]));
			else
			    fprintf(fp, " 0,");
		    }
		    fprintf(fp, " },\n");
		    if (ap->defString)
			fprintf(fp, "            \"%s\",\n", ap->defString);
		    else
			fprintf(fp, "            0,\n");
		    if (ap->defAtom)
			fprintf(fp, "            %s\n",
				GetAtomVal(ap->defAtom));
		    else
			fprintf(fp, "            0\n");
		}
		else {
		    fprintf(fp, "  0, 0, 0, 0,\n"
				"            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },"
				"\n            0,\n"
				"            0\n");
		}
		fprintf(fp, "        },\n");
	    }
	    fprintf(fp, "      },\n      { ");

	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->attrList[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,");
	    }
	    fprintf(fp, " },\n      { ");
	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->reqAttrs[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,");
	    }
	    fprintf(fp, " },\n      { ");
	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->fixedAttrs[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,");
	    }
	    fprintf(fp, " },\n      { ");
	    for (j = 0; j < 10; j++) {
		atom at = eTable[i]->impliedAttrs[j];

		if (at)
		    fprintf(fp, " %s,", GetAtomVal(at));
		else
		    fprintf(fp, " 0,");
	    }
	    fprintf(fp, " },\n    },\t\t\t\t\t/* End of element %s */\n\n",
		    GetAtomVal(i));
	}
    }
    fprintf(fp, "};\n");
}

/*
 * Add an element to the list.  If list is 0, create it.
 */
ListEle *
AddListEle(ListEle *list, atom element)
{
    ListEle *new = (ListEle *)malloc(sizeof(ListEle));
    ListEle *lp;

    new->element = element;
    new->next = 0;

    if (list == 0)
	return (new);
    else {
	for (lp = list; lp->next; lp = lp->next)
	    ;
	lp->next = new;
	return (list);
    }
}

List *
AddList(List *list, atom element)
{
    if (list == 0) {
	list = (List *)malloc(sizeof(List));
	list->first = 0;
    }
    list->first = AddListEle(list->first, element);
    return (list);
}

ListEle *
DeleteListEle(ListEle *list, atom element)
{
    if (list == 0) {
	return (0);
    }
    else if (list->element == element) {
	ListEle *next = list->next;

	free(list);
	return (DeleteListEle(next, element));
    }
    else {
	list->next = DeleteListEle(list->next, element);
	return (list);
    }
}

List *
DeleteList(List *list, atom element)
{
    if (list == 0 || list->first == 0) {
	return (0);
    }
    else {
	list->first = DeleteListEle(list->first, element);
	if (list->first == 0) {
	    free (list);
	    return (0);
	}
    }
    return (list);
}

atom
PopList(List *list)
{
    atom at;
    ListEle *next;

    if (list == 0 || list->first == 0) {
	return (0);
    }

    at = list->first->element;
    next = list->first->next;
    free(list->first);
    list->first = next;
    return (at);
}

List *
sumLists(List *list1, List *list2)
{
    atom at;
    List *new = 0;

    while ((at = PopList(list1)) != 0)
	new = AddList(new, at);
    while ((at = PopList(list2)) != 0)
	new = AddList(new, at);
    return (new);
}

List *
subLists(List *list1, List *list2)
{
    atom at;
    List *new = 0;

    while ((at = PopList(list2)) != 0)
	list1 = DeleteList(list1, at);
    return (list1);
}

PrintList(List *list)
{
    ListEle *lp = list->first;

    fprintf(stdout, " (");
    while (lp) {
	fprintf(stderr, " %s", GetAtomVal(lp->element));
	lp = lp->next;
    }
    fprintf(stdout, " )");
}

static void
newElement(atom name, int type1, int type2, List *list)
{
    DTDElement *ep = (DTDElement *)calloc(1, sizeof(DTDElement));
    int i = 0;

    ep->name = name;
    if (type1 == 1)
	ep->flags |= E_ELE_OPT;
    if (type2 == 1) {
	if (list)
	    ep->flags |= E_TERM_OPT;
	else
	    ep->flags |= E_TERM_NONE;
    }
    else {
	ep->flags |= E_TERM_MAND;
    }

    if (list) {
	ListEle *lp = list->first;

	while (lp) {
	    ep->children[i++] = lp->element;
	    lp = lp->next;
	}
    }
    eTable[name] = ep;
}

static DTDAttribute *
newAttribute(atom name, int type, unsigned int valid, atom dataType,
	     List *list, char *defString, atom defAtom)
{
    DTDAttribute *ap = (DTDAttribute *)calloc(1, sizeof(DTDAttribute));
    int i = 0;

    ap->name = name;
    ap->type = type;
    ap->valid = valid;
    if (valid & AF_DATATYPE)
	ap->dataType = dataType;
    if (valid & AF_DEFSTRING)
	ap->defString = defString;
    if (valid & AF_DEFATOM)
	ap->defAtom = defAtom;
    if (valid & AF_LIST) {
	ListEle *lp = list->first;
 
	while (lp) {
	    ap->list[i++] = lp->element;
	    lp = lp->next;
	}
    }
    return (ap);
}

static void
insertAttr(atom name, List *list)
{
    ListEle *lp = list->first;
    int i = 0;

    while (lp) {
	DTDAttribute *ap = (DTDAttribute *)(lp->element);

	eTable[name]->attrList[i] = ap->name;	/* Copy name */
	eTable[name]->attrs[i++] = *ap;		/* Copy contents */
	lp = lp->next;
    }
}

/* Fill in the parents table for every element */
static void
GenerateParents()
{
    int i, j, k;

    for (i = 1; GetAtomVal(i); i++) {
	if (eTable[i]) {
	    atom at = eTable[i]->name;

	    for (j = 0; j < 40; j++) {
		atom child = eTable[i]->children[j];

		if (child == 0)
		    break;

		if (child != GetAtom("PCDATA") && child != GetAtom("CDATA")) {
		    for (k = 0; eTable[child]->parents[k] && (k < 40);  k++)
			;
		    eTable[child]->parents[k] = at;
		}
	    }
	}
    }
}

/* Create the required attribute list in each element */
static void
OrganizeAttrs()
{
    int i, j;
    DTDAttribute *ap;

    for (i = 1; GetAtomVal(i); i++) {
	if (eTable[i]) {
	    int reqIndex = 0, impliedIndex = 0, fixedIndex = 0;

	    for (j = 0; eTable[i]->attrs[j].name; j++) {
		if (eTable[i]->attrs[j].type == A_REQUIRED)
		    eTable[i]->reqAttrs[reqIndex++] = eTable[i]->attrs[j].name;
		else if (eTable[i]->attrs[j].type == A_FIXED)
		    eTable[i]->fixedAttrs[fixedIndex++] =
						    eTable[i]->attrs[j].name;
		else if (eTable[i]->attrs[j].type == A_IMPLIED)
		    eTable[i]->impliedAttrs[impliedIndex++] =
						    eTable[i]->attrs[j].name;
	    }
	}
    }
}
